import {Anntation, BeanDefine, Clazz, FieldDefine}  from './SpringType'
import { Autowired,AutowiredParam,Value, ValueParam } from './SpringAnnotation'
import { geFormatValue } from './SpringResource'
import { isFunction, isPlainObject } from './shared';

//对象缓存
const beanDefineMap = new Map<BeanDefine,any>()

//后置处理器集合
const beanPostProcessorList = new Set<BeanPostProcessor>();

//装配中的类 防止循环引用
const assembleingClass = new Set<Clazz>();

//额外绑定的class，防止类信息被treeshake掉
const bindBeanClazzList = new Set<Clazz>();

//绑定的beanProcessor class
const bindBeanProcessorClazzList = new Set<new()=>BeanPostProcessor>();

//bean定义集合
export const beanDefineList = new Set<BeanDefine>([]);

//id映射
export const idBeanDefineMap = new Map<string,BeanDefine>()

//bean的生命周期函数
export interface SpringBean {
    // [SpringEnum.__isPringBean__]:true
     //当属性装配完毕
     onAttrMounted():void;
 }

//bean的后置处理器 用来做AOP
export interface BeanPostProcessor  {

    //获取序号
    getSort():number;

    //属性装配之前
    postProcessBeforeInitialization(bean:any,beanDefine:BeanDefine):Object

    //属性装配之后
    postProcessAfterInitialization(bean:any,beanDefine:BeanDefine):Object

}

function isBeanPostProcessorClass(clazz:Clazz):boolean{
    const post = new clazz() as BeanPostProcessor
    return isFunction(post.getSort) && isFunction(post.postProcessAfterInitialization) && isFunction(post.postProcessBeforeInitialization);
}

//根据类获取定义
export const getBeanDefineByClass = function(clazz:Clazz):BeanDefine|undefined{
    return Array.from(beanDefineList).find(b => b.clazz == clazz);
}

//替换类
export function replaceClazz<T,E extends T>(clazz:new()=>T,target:new()=>E):number{

    const fieldDefineList:FieldDefine[] = Array.from(beanDefineList).map(bd => bd.fieldList).reduce((s,v)=> s.concat(v),[]);
    const allFieldAnnotation:Anntation[] = fieldDefineList.map(f => f.annotationList).reduce((s,v)=> s.concat(v) ,[]);

    const matchAnnotation = allFieldAnnotation.filter(a => a.clazz === Autowired).filter(a => (a.params as AutowiredParam<any>).clazz == clazz)

    if(matchAnnotation.length == 0)
        throw Error(`[ReplaceClazzError] not match class ${clazz}`)

    matchAnnotation.forEach(a => (a.params as AutowiredParam<any>).clazz = target)

    return matchAnnotation.length;
    
}

//根据bean获取定义
export const getBeanDefineByBean = function(bean:any):BeanDefine|null{
    
    let bd:BeanDefine|null = null;

    beanDefineMap.forEach((mapBd,mapBean) => {

        if(mapBean === bean)
            bd = mapBd;

    })

    return bd;
}

//spring上下文
export abstract class SpringContainer {
    getBeanDefineMap(){
        return beanDefineMap;
    }
    getBeanPostProcessor(){
        return getSoredBeanPostProcessorList()
    }
} 

//装配指定class
export const assemble = function (clazz:Clazz){

    const bd = getBeanDefineByClass(clazz)

    if(!bd){
        throw Error(`can not find class ${clazz},maybe you forgot to add @Component!`)
    }

    const app = assembleBeanDefine(bd);

    return app;
}


//获取排序好的后指处理器
function getSoredBeanPostProcessorList():BeanPostProcessor[]{
    return Array.from(beanPostProcessorList).sort((v1,v2) => v1.getSort()-v2.getSort());
}

//装配指定beanDefine
function assembleBeanDefine(bd:BeanDefine):any{

    if(beanDefineMap.has(bd))
        return beanDefineMap.get(bd);

        //防止循环引用 加入装配记录
    if(assembleingClass.has(bd.clazz))
        throw Error(`clazz ${bd.clazz} already assemble`)

    assembleingClass.add(bd.clazz);

    //实例化对象 只支持无参构造器
    let bean = new bd.clazz();

    //获取后置处理器
    const beanPostProcessor = getSoredBeanPostProcessorList()

    //bean处理器处理装配前操作
    beanPostProcessor.forEach(post => bean = post.postProcessBeforeInitialization(bean,bd))

    //字段属性装配和注入
    bd.fieldList.forEach(fieldBd => {
      
        const fieldName = fieldBd.name;
        fieldBd.annotationList.forEach(anno => {

            //处理装配 依据class类型
            if(anno.clazz === Autowired){
                const param = anno.params as AutowiredParam<Clazz>;
                const id = param.id;
                const clazz = param.clazz;
                const force = param.force;
                let subApp;
                
                //处理装配 依据id
                if(id){
                    const targetBd = idBeanDefineMap.get(id);
                    if(!targetBd){
                        throw Error(`[ID_NOT_FIND_ERROR] @ComponentId('${id}') not find!`)
                    }
                    if(clazz !== targetBd.clazz){
                        throw Error(`[CLAZZ_MATCH_ERROR] id '${id}' class must be ${clazz}` )
                    }
                    subApp = assembleBeanDefine(targetBd)
                }
                
                //强制装配
                if(force){
                    subApp = assemble(param.clazz);
                }else{
                    const oldValue = (bean as any)[fieldName]
                    if(!isPlainObject(oldValue))
                        throw Error(`[NOT_FORCE] class[${bd.clazz}] field[${fieldName}] autowired.force=false must be set object!`)
                    const fieldClazzBd = getBeanDefineByClass(param.clazz)
                    if(fieldClazzBd){
                        subApp = assembleBeanDefine(fieldClazzBd);
                    }else{
                        return;
                    }
                }

                // const subApp = assemble(param.clazz);
                if(!Reflect.set(bean,fieldName,subApp)){
                    throw Error(`[REFLECT_PROPERTY_SET_ERROR]:class:${clazz.name} field:${fieldName}`)
                }
            }

            //处理自动注入
            if(anno.clazz === Value){
                const param = anno.params as ValueParam;
                if(!Reflect.set(bean,fieldName,geFormatValue(param.path,param.type))){
                    throw Error('annotation Value error')
                }
            }
        })
    });

    //调用生命周期函数
    (bean as SpringBean).onAttrMounted?.();

    //bean处理器处理装配后操作
    beanPostProcessor.forEach(post => bean = post.postProcessAfterInitialization(bean,bd))

    //删除引用
    assembleingClass.delete(bd.clazz);

    beanDefineMap.set(bd,bean);

    return bean;
}

//清空工厂的引用 主要测试使用
export const cleanBeanCache = ()=>{

    beanDefineMap.clear();

}

//绑定额外的class
export const addExtBeanClazz = (clazz:Clazz)=>{

    if(isBeanPostProcessorClass(clazz)){
        bindBeanProcessorClazzList.add(clazz)
    }else{
        bindBeanClazzList.add(clazz);
    }
  
}

//将所有绑定的class,实例化到beanDefineMap中
const instanceExtClazz = ()=>{
    Array.from(bindBeanClazzList).map(assemble);
}

//实例化后置处理器
const instanceBeanPostProcessor = ()=>{
    Array.from(bindBeanProcessorClazzList).map(assemble).forEach(b => beanPostProcessorList.add(b))
}

//装配bean
export function getBean<T>(clazz:new()=>T):T{
    beanFactoryInit();
    return assemble(clazz);
}

export const beanFactoryInit = ()=>{

    instanceExtClazz();

    instanceBeanPostProcessor();

}